#include "OP_CreateTriangle.h"
#include "../Geometries/PolygonMesh.h"

using namespace std;
using namespace CICore;
using namespace CIGeom;
using namespace CIView;

///<************************************************************************************************************>
///<************************************* region CONSTANTS DEFINITION ******************************************>
///<************************************************************************************************************>

/***************************************************************************************************************
************************************** endregion CONSTANTS DEFINITION ******************************************
****************************************************************************************************************/






/// <summary> 
/// Constructor 
/// </summary>
/// <param name="v"> A reference to the vector that contains the four 3D views pointers </param>
CIOps::OP_CreateTriangle::OP_CreateTriangle (vector<CIView::View*>& v)
: Operation (v), geometry (0), face(0), vertexCounter (0), triangle_vertices (3) {

    newSelectionMode = Geometry::VERTICES_SELECTION;
}

/// <summary> 
/// Destructor 
/// </summary>
CIOps::OP_CreateTriangle::~OP_CreateTriangle() {
    if (status != COMPLETED ) {
        if (geometry) {
            //Deleting the created geometry and face
            geometry->GetVertices().clear();
            geometry->GetFaces().clear();
            delete geometry;
            delete face;
        }
    }
    else {
        //Deleting the old geometries 
        map<Geometry*,bool>::iterator it = unifiedGeometries.begin();
        while (it != unifiedGeometries.end()) {
            it->first->GetVertices().clear();
            it->first->GetFaces().clear();
            delete it->first;
            it++;
        }
    }
}










///<************************************************************************************************************>
///<**************************************** region Utility Methods ********************************************>
///<************************************************************************************************************>

/// <summary> 
/// Method called to execute directly the operation 
/// </summary>
/// <returns> The operation status </returns>
int CIOps::OP_CreateTriangle::Exec() {
    switch (status) {
        case INITIAL:
            status = WAITING_FOR_EVENTS;
            break;
    }
    return status;        
}

/// <summary> 
/// Method called to undo the operation 
/// </summary>
void CIOps::OP_CreateTriangle::Undo() {
    status = UNDO;          

    scene.RemoveGeometry (geometry->GetName().c_str());           //Removing the new geometry from the scene

    //Adding the old geometries in the scene
    map<Geometry*,bool>::iterator it = unifiedGeometries.begin();
    while (it != unifiedGeometries.end()) {
        scene.AddGeometry (it->first);
        it++;
    }

    RestoreOldSelection();                              //Restores the old selection status    
    
    UpdateViews();                                      //Updates the 3D views
}

/// <summary> 
/// Method called to redo the operation 
/// </summary>
void CIOps::OP_CreateTriangle::Redo() {
    status = COMPLETED;        

    scene.AddGeometry (geometry);           //Adds the new geometry in the scene

    //Removing the unified geometries from the scene
    map<Geometry*,bool>::iterator it = unifiedGeometries.begin();
    while (it != unifiedGeometries.end()) {
        scene.RemoveGeometry (it->first->GetName().c_str());
        it++;
    }

    RestoreNewSelection();                          //Restore the new selectio status
    UpdateViews();                                  //Updates the 3D views
}

/// <summary> 
/// Mouse Pressed event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse press event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Mouse Pressed event </param>
void CIOps::OP_CreateTriangle::MousePressEvent (CIView::View* view, QMouseEvent* e) {
    if (vertexCounter == 0) {
        SaveOldSelection();                          //Saves the old selection status    
        scene.DeselectAllFaces();                    //Deselects all faces       //TODO -> Deselect all (included geometries and groups)

        scene.SetSelectionMode (newSelectionMode);

        UpdateViews();                                  //Updates the 3D views
    }
}

/// <summary> 
/// Mouse Released event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse release event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Mouse Released event </param>
void CIOps::OP_CreateTriangle::MouseReleaseEvent (CIView::View* view, QMouseEvent* e) {
    float x = e->x();
    float y = e->y();
    
    //Makes selection of a single vertex and retrieves a pair containing the geometry
    //to which the vertex belong and the index of the vertex within the geometry vertices library
    pair<Geometry*,int> pair = view->SelectVertex (x, y);   
    Geometry* g = pair.first;
    int v_index = pair.second;
    Vertex* v = 0;

    if (g) {                                    //If a vertex has been selected
        v = g->GetVertices().at (v_index);      //retrieves a pointer to the selected vertex
        triangle_vertices[vertexCounter] = v;   //inserts the pointer in the triangle vertices structure

        if (vertexCounter == 0) {                                   //If it's the first vertex of the triangle
            unsigned int id = scene.GenerateNewGeometryID();        //gets a reference to the number of geometries that have been added to the scene
            stringstream name;
            name << "Mesh" << id; 
            geometry = new PolygonMesh (name.str());                //creates the new geometry                    
        }

        if (!unifiedGeometries[g]) {
            //Inserting all vertices of the old geometry in the new geometry
            VertexLib& oldVertexLib = g->GetVertices();
            VertexLib& newVertexLib = geometry->GetVertices();
            newVertexLib.insert (newVertexLib.begin()+newVertexLib.size(), oldVertexLib.begin(), oldVertexLib.end());  

            //Inserting all faces of the old geometry in the new geometry
            FaceLib& oldFaceLib = g->GetFaces();
            FaceLib& newFaceLib = geometry->GetFaces();
            newFaceLib.insert (newFaceLib.begin()+newFaceLib.size(), oldFaceLib.begin(), oldFaceLib.end()); 
        }
        unifiedGeometries[g] = true;            //Stores the info about the geometries to unify through the triangle creation

        vertexCounter++;
    }

    if (vertexCounter == 3) {                   //If this vertex is the third one
        status = COMPLETED;                     //sets the operation status to COMPLETED  

        //Creating and adding the triangle
        face = new Face (triangle_vertices);        
        geometry->AddFace (face);
        geometry->UpdateBoundingBox();
        geometry->ComputeNormals();

        scene.AddGeometry (geometry);           //adds the new geometry in the scene

        //Removing the unified geometries from the scene
        map<Geometry*,bool>::iterator it = unifiedGeometries.begin();
        while (it != unifiedGeometries.end()) {
            scene.RemoveGeometry (it->first->GetName().c_str());
            it++;
        }
        view->SetPrimitivesSelected (true);                     //Signals that there is a primitive selected
        
        SaveNewSelection();                     //saves the new selection status  
    }
    else if (vertexCounter)
        status = STEP_COMPLETED;                //sets the operation status to STEP_COMPLETED
    else
        status = ZOMBIE;

    UpdateViews();                              //Updates the 3D views  
}

/// <summary> 
/// Mouse Moved event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse move event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Mouse Moved event </param>
void CIOps::OP_CreateTriangle::MouseMoveEvent (CIView::View* view, QMouseEvent* e) {
}

/// <summary> 
/// Wheel event handler, called whenever the operation 
/// is in execution and a 3D view intercepts a mouse wheel event
/// </summary>
/// <param name="view"> The view that has thrown the event </param>
/// <param name="e"> Wheel event </param>
void CIOps::OP_CreateTriangle::WheelEvent (CIView::View* view, QWheelEvent* e) {
}
/***************************************************************************************************************
***************************************** endregion Utility Methods ********************************************
****************************************************************************************************************/


